#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>

#include "bloom_filter.h"
#include "type_routingtable.h"


static FILE * config_file = NULL;

static char line_buf[2048]; 
static char read_buf[2048]; 
static int reread_buf = 0; 

static  void parse_host() ;
static void parse_groups();
static void parse_LBF_neighbors(u_int32_t group_addr);

static char * read_line() ;
static void unread_line(); 

void read_typecast_config_file()
{
    config_file = fopen("typecast.conf", "r");
    if (config_file == NULL)
    {
        perror("typecast.conf not found.\n");
        exit(-1);
    }
	parse_host(); 
}

static void parse_host() 
{
	struct in_addr in; 
	char * line; 
	char *token; 
	
	u_int32_t local_ip = get_local_ip_address();     
	while((line = read_line()) != NULL) {      
	    char * token = strsep(&line, ":=");
	    if (strcmp(token, "host") == 0) { 
	    	// Check whether this host is the equivalent to the local host
	    	char * addr = line; 
	    	// skip white spaces
	    	while( (*addr == ' ') || (*addr == '\t') ) { 
	    		addr++; 
	    	}
			if (inet_aton(addr, &in) == 0) { 
				perror("Error when converting network address"); 
				exit(-1); 
			}
			if (ntohl(in.s_addr) != local_ip) { 
				continue; 
			}
			parse_groups(); 
			return; 
	    }
    }    
    fprintf(stderr, "No host entry found for this host, check your configuration file\n"); 
    exit(-1); 
}

static void parse_groups()
{ 
	struct in_addr in; 
	char * line; 
	char *token; 
	int num_groups = 0; 
	
	while((line = read_line()) != NULL) {      
	    token = strsep(&line, ":=");
	    if (strcmp(token, "group") == 0) { 
	    	char * addr = line; 
	    	// skip white spaces
	    	while( (*addr == ' ') || (*addr == '\t') ) { 
	    		addr++; 
	    	}
			if (inet_aton(addr, &in) == 0) { 
				perror("Error when converting network address"); 
				exit(-1); 
			}
			parse_LBF_neighbors(ntohl(in.s_addr)); 
			num_groups++; 
	    } else { 
			unread_line(); 
			if (num_groups == 0) { 
				fprintf(stderr, "No group engry found for this host, check your configuration file\n"); 
				exit(-1); 
			}
			return; 
	    }
	}	    	
		
}

static void parse_LBF_neighbors(u_int32_t group_addr)
{
	struct in_addr in; 
	char * line; 
	char *token; 
	bloom_filter_t filter; 
	u_int32_t neighbor_addr; 
	int lbf_found = 0; 
	int num_neighbors = 0; 
		
	while((line = read_line()) != NULL) {      
	    token = strsep(&line, ":=");
	    if (strcmp(token, "LBF") == 0) { 
	    	char * lbf  =  line; 
	    	// skip white spaces
	    	while( (*lbf == ' ') || (*lbf == '\t') ) { 
	    		lbf++; 
	    	}
	    	hex_string_to_bloom_filter(lbf, filter); 
	    	InsertMulticastGroup(group_addr, filter); 
	    	lbf_found = 1; 
	    } else { 
	    	if (strcmp(token, "neighbors") == 0) { 
	    		while((*line == ' ') || (*line == '\t')) { 
	    			line++; 
	    		}
	    		char * addr;
	    		init_bloom_filter(filter); 
	    		while(line != NULL) { 
	    			addr = strsep(&line, " \t"); 
					if (inet_aton(addr, &in) == 0) { 
						perror("Error when converting network address"); 
						exit(-1); 
					}
					neighbor_addr = ntohl(in.s_addr); 
					InsertMulticastNeighbor(group_addr, neighbor_addr, filter); 
	    		}
	    		num_neighbors++; 
	    	} else { 
	    		unread_line(); 
	    		if (lbf_found == 0) { 
	    			fprintf(stderr, "No LBF found for this host, check your config file\n"); 
	    			exit(-1); 
	    		} 
	    		if (num_neighbors == 0) { 
	    			fprintf(stderr, "No neighbors entry found for this host, check your config file\n"); 
	    			exit(-1); 
	    		}
	    		return; 
	    	}
	    }
	}									
}

/**
 * Read a non-empty and non-comment line from a File. Return NULL if 
 * error occurs or end-of-file.
 */
 
char * read_line() 
{ 
	int len; 
	if (reread_buf) { 
		reread_buf = 0; 
		strcpy(line_buf, read_buf); 
		return line_buf; 
	}
	
	while(fgets(read_buf, sizeof(read_buf), config_file) != NULL) { 
		if (read_buf[0] == '\0' || read_buf[0] == '#' || read_buf[0] == ' ' || read_buf[0] == '\t' || read_buf[0] == '\n') { 
			continue; 
		}
		len = strlen(read_buf); 
		if (read_buf[len-1] == '\n') { 
			read_buf[len-1] = '\0'; 
		}
		
		strcpy(line_buf, read_buf); 
		return line_buf; 
	}
	return NULL; 
}

void unread_line() 
{
	reread_buf = 1; 
} 